using System;
using System.Collections.Generic;
using System.Data;
using System.Text;
using System.Xml.Serialization;

namespace Common.Utilities.Database
{
    /// <summary>
    /// 
    /// </summary>
    [Serializable]
    public class  DbTableFieldStats
    {
        #region fields
        private DbTableFieldKey _FieldKey;
        private string _Max;
        private string _Min;
        private string _Mean;
        private string _Median;
        private int _DistinctCount;
        private bool _Indexed;
        private FieldDataType _FieldDataType;
        private Dictionary<string, int> _DistributionByDistinctValue;
        private Dictionary<Range<DateTime>, int> _DistributionByTimeRange;
        private Dictionary<Range<decimal>, int> _DistributionByNumberRange;
        #endregion

        #region ctor
        /// <summary></summary>
        public DbTableFieldStats()
        {
            this._FieldKey = new DbTableFieldKey();
            this._Max = "";
            this._Min = "";
            this._Mean = "";
            this._Median = "";
            this._DistinctCount = 0;
            this.Indexed = false;
            FieldDataType fieldType = new FieldDataType();
            fieldType.DataType = SqlDbType.Int;
            fieldType.Nullible = true;
            fieldType.Precision = 0;
            fieldType.Scale = 0;
            fieldType.Size = 4;
            this.FieldDataType = fieldType;
            this._DistributionByDistinctValue = new Dictionary<string, int>();
            this._DistributionByNumberRange = new Dictionary<Range<decimal>, int>();
            this._DistributionByTimeRange = new Dictionary<Range<DateTime>, int>();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="fieldKey"></param>
        /// <param name="max"></param>
        /// <param name="min"></param>
        /// <param name="mean"></param>
        /// <param name="median"></param>
        /// <param name="distinctCount"></param>
        /// <param name="indexed"></param>
        /// <param name="fieldType"></param>
        /// <param name="countByDistinctValue"></param>
        /// <param name="countByTimeSpan"></param>
        /// <param name="countByNumberRange"></param>
        public DbTableFieldStats ( DbTableFieldKey fieldKey, string max, string min, string mean, 
            string median, int distinctCount, bool indexed, FieldDataType fieldType, 
            Dictionary<string,int> countByDistinctValue, 
            Dictionary<Range<DateTime>,int> countByTimeSpan, 
            Dictionary<Range<decimal>,int> countByNumberRange)
        {
            this.FieldKey = fieldKey;
            this.Max = max;
            this.Min = min;
            this.Mean = mean;
            this.Median = median;
            this.DistinctCount = distinctCount;
            this.Indexed = indexed;
            this.FieldDataType = fieldType;
            this._DistributionByDistinctValue = countByDistinctValue;
            this._DistributionByTimeRange = countByTimeSpan;
            this._DistributionByNumberRange = countByNumberRange;
        }

        #endregion

        #region props
        /// <summary></summary>
        public DbTableFieldKey FieldKey
        {
            get { return _FieldKey; }
            set { _FieldKey = value; }
        }

        /// <summary></summary>
        public string Max
        {
            get { return _Max; }
            set { _Max = value; }
        }

        /// <summary></summary>
        public string Min
        {
            get { return _Min; }
            set { _Min = value; }
        }

        /// <summary></summary>
        public string Mean
        {
            get { return _Mean; }
            set { _Mean = value; }
        }

        /// <summary></summary>
        public string Median
        {
            get { return _Median; }
            set { _Median = value; }
        }

        /// <summary></summary>
        public int DistinctCount
        {
            get { return _DistinctCount; }
            set { _DistinctCount = value; }
        }

        /// <summary></summary>
        public StringIntPair[] DistributionByDistinctValue
        {
            get
            {
                if(this._DistributionByDistinctValue ==null || this._DistributionByDistinctValue.Count ==0)
                {
                    return null;
                }
                else
                {
                    StringIntPair[] pairs = new StringIntPair[this._DistributionByDistinctValue.Count];
                    string[] keys = new string[this._DistributionByDistinctValue.Count];
                    this._DistributionByDistinctValue.Keys.CopyTo(keys, 0);
                    for (int i = 0; i < keys.Length; i++)
                    {
                        StringIntPair pair = new StringIntPair();
                        pair.Key = keys[i];
                        pair.Value = this._DistributionByDistinctValue[keys[i]];
                        pairs[i] = pair;
                    }
                    return pairs;
                }
            }
            set
            {
                this._DistributionByDistinctValue = new Dictionary<string, int>();
                if(value !=null && value.Length>0)
                {
                    for(int i=0;i<value.Length;i++)
                    {
                        string key = value[i].Key;
                        int count = value[i].Value;
                        this._DistributionByDistinctValue.Add(key, count);
                    }
                }
            }
        }
        
        /// <summary>
        /// 
        /// </summary>
        public TimeRangeIntPair[] DistributionByTimeRange
        {
            get
            {
                if(this._DistributionByTimeRange !=null && this._DistributionByTimeRange.Count>0)
                {
                    TimeRangeIntPair[] pairs = new TimeRangeIntPair[this._DistributionByTimeRange.Count];
                    Range<DateTime>[] ranges = new Range<DateTime>[this._DistributionByTimeRange.Count];
                    this._DistributionByTimeRange.Keys.CopyTo(ranges, 0);
                    for(int i=0;i<ranges.Length;i++)
                    {
                        TimeRangeIntPair pair = new TimeRangeIntPair();
                        pair.TimeRange = ranges[i];
                        pair.Value = this._DistributionByTimeRange[ranges[i]];
                        pairs[i] = pair;
                    }
                    return pairs;
                }
                else
                {
                    return null;
                }
            }
            set
            {
                this._DistributionByTimeRange = new Dictionary<Range<DateTime>, int>();
                if(value !=null && value.Length>0)
                {
                    for(int i=0;i<value.Length;i++)
                    {
                        Range<DateTime> range = value[i].TimeRange;
                        int count = value[i].Value;
                        this._DistributionByTimeRange.Add(range, count);
                    }
                }
            }
        }
        
        /// <summary>
        /// 
        /// </summary>
        public NumberRangeIntPair[] DistributionByNumberRange
        {
            get
            {
                if(this._DistributionByNumberRange ==null || this._DistributionByNumberRange.Count==0)
                {
                    return null;
                }
                else
                {
                    NumberRangeIntPair[] pairs = new NumberRangeIntPair[this._DistributionByNumberRange.Count];
                    Range<decimal>[] ranges = new Range<decimal>[this._DistributionByNumberRange.Count];
                    this._DistributionByNumberRange.Keys.CopyTo(ranges, 0);
                    for(int i=0;i<ranges.Length;i++)
                    {
                        NumberRangeIntPair pair = new NumberRangeIntPair();
                        pair.NumberRange = ranges[i];
                        pair.Value = this._DistributionByNumberRange[ranges[i]];
                        pairs[i] = pair;
                    }
                    return pairs;
                }
            }
            set
            {
                this._DistributionByNumberRange = new Dictionary<Range<decimal>, int>();
                if(value !=null && value.Length>0)
                {
                    for(int i=0;i<value.Length;i++)
                    {
                        Range<decimal> range = value[i].NumberRange;
                        int count = value[i].Value;
                        this._DistributionByNumberRange.Add(range, count);
                    }
                }
            }
        }

        /// <summary></summary>
        public bool Indexed
        {
            get { return _Indexed; }
            set { _Indexed = value; }
        }

        /// <summary></summary>
        public FieldDataType FieldDataType
        {
            get { return _FieldDataType; }
            set { _FieldDataType = value; }
        }

        #endregion

        #region static methods
        /// <summary>
        /// 
        /// </summary>
        /// <param name="fieldKey"></param>
        /// <returns></returns>
        public static DbTableFieldStats BuildSimpleTableFieldStatistics(DbTableFieldKey fieldKey)
        {
            TableColumn col = DbUtil.GetTableColumn(fieldKey.ConnStr, fieldKey.TblName, fieldKey.FieldName);
            string max = string.Empty;
            string min = string.Empty;
            string mean = string.Empty;
            string median = string.Empty;
            bool indexed = DbUtil.IsTableColumnIndexed(fieldKey.ConnStr, fieldKey.TblName, fieldKey.FieldName);
            int distinctCount =
                DbUtil.GetTableFieldDistinctValueCount(fieldKey.DbProvider, fieldKey.ConnStr, fieldKey.SchemaName,
                                                       fieldKey.TblName, fieldKey.FieldName);
            FieldDataType fieldType = new FieldDataType();
            fieldType.DataType = col.DbType;
            fieldType.Nullible = col.AllowNull;
            fieldType.Precision = col.Precision;
            fieldType.Scale = col.Scale;
            fieldType.Size = col.Size;
            Dictionary<string, int> countByValue = new Dictionary<string, int>();
            Dictionary<Range<DateTime>, int> countByTimeSpan = new Dictionary<Range<DateTime>, int>();
            Dictionary<Range<decimal>, int> countByNumberRange = new Dictionary<Range<decimal>, int>();
            if(FieldDataType.IsNumber(col.DbType))
            {
                max = "0";
                min = "0";
                mean = "0";
                median = "0";
            }
            else if(FieldDataType.IsDateTime(col.DbType))
            {
                max = (new DateTime(2099, 12, 31)).ToString();
                min = (new DateTime(1900, 1, 1)).ToString();
                mean = min;
                median = min;
            }

            return
                new DbTableFieldStats(fieldKey, max, min, mean, median, distinctCount,
                                      indexed, fieldType, countByValue, countByTimeSpan,
                                      countByNumberRange);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="fieldKey"></param>
        /// <returns></returns>
        public static DbTableFieldStats BuildTableFieldStatistics(DbTableFieldKey fieldKey)
        {
            TableColumn col = DbUtil.GetTableColumn(fieldKey.ConnStr, fieldKey.TblName, fieldKey.FieldName);
           
            bool indexed = DbUtil.IsTableColumnIndexed(fieldKey.ConnStr, fieldKey.TblName, fieldKey.FieldName);
            int distinctCount = 0;
                
            FieldDataType fieldType = new FieldDataType();
            fieldType.DataType = col.DbType;
            fieldType.Nullible = col.AllowNull;
            fieldType.Precision = col.Precision;
            fieldType.Scale = col.Scale;
            fieldType.Size = col.Size;

            string min = string.Empty;
            string max = string.Empty;
            string mean = string.Empty;
            string median = string.Empty;

            if(FieldDataType.IsDateTime(col.DbType) || FieldDataType.IsNumber(col.DbType) || FieldDataType.IsStringType(col.DbType))
            {
                max = DbUtil.GetTableFieldMaxValue(fieldKey.DbProvider, fieldKey.ConnStr, fieldKey.SchemaName,
                                                          fieldKey.TblName, fieldKey.FieldName);
                min = DbUtil.GetTableFieldMinValue(fieldKey.DbProvider, fieldKey.ConnStr, fieldKey.SchemaName,
                                                          fieldKey.TblName, fieldKey.FieldName);

                if(FieldDataType.IsDateTime(col.DbType) || FieldDataType.IsNumber(col.DbType))
                {
                    mean =
                        DbUtil.GetTableFieldAverageValue(fieldKey.DbProvider, fieldKey.ConnStr, fieldKey.SchemaName,
                                                         fieldKey.TblName, fieldKey.FieldName);
                    median =
                        DbUtil.GetTableFieldMedianValue(fieldKey.DbProvider, fieldKey.ConnStr, fieldKey.SchemaName,
                                                        fieldKey.TblName, fieldKey.FieldName);
                }
            }
            
            

            Dictionary<string, int> countByValue = new Dictionary<string, int>();
            Dictionary<Range<DateTime>, int> countByTimeSpan = new Dictionary<Range<DateTime>, int>();
            Dictionary<Range<decimal>, int> countByNumberRange = new Dictionary<Range<decimal>, int>();
            if (distinctCount < 20 && (!FieldDataType.IsBinaryType(col.DbType)) && (!FieldDataType.IsTextType(col.DbType)))
            {
                distinctCount =
                    DbUtil.GetTableFieldDistinctValueCount(fieldKey.DbProvider, fieldKey.ConnStr, fieldKey.SchemaName,
                                                           fieldKey.TblName, fieldKey.FieldName);
                countByValue =
                    DbUtil.GetTableFieldCountByDistinctValue(fieldKey.DbProvider, fieldKey.ConnStr, fieldKey.SchemaName,
                                                           fieldKey.TblName, fieldKey.FieldName);

                if (FieldDataType.IsNumber(col.DbType))
                {
                    countByNumberRange =
                        DbUtil.GetTableFieldCountByNumberRange(fieldKey.DbProvider, fieldKey.ConnStr,
                                                               fieldKey.SchemaName, fieldKey.TblName, fieldKey.FieldName);
                }
                else if (FieldDataType.IsDateTime(col.DbType))
                {
                    countByTimeSpan =
                        DbUtil.GetTableFieldCountByTimeRange(fieldKey.DbProvider, fieldKey.ConnStr, fieldKey.SchemaName,
                                                             fieldKey.TblName, fieldKey.FieldName);
                }
            }

            return
                new DbTableFieldStats(fieldKey, max, min, mean, median, distinctCount,
                                      indexed, fieldType, countByValue, countByTimeSpan,
                                      countByNumberRange);
        }
        #endregion

        #region overrides 
        
        #endregion 
    }
}
